#include "database/redis_pool.h"
#include "LoggerManager.h"
#include <hiredis/hiredis.h>
#include <cstring>
#include <cstdlib>

namespace cpp_backend
{
    namespace database
    {

        using namespace logger;

        // RedisConnection实现

        RedisConnection::RedisConnection(const std::string &host, int port, const std::string &password)
            : host_(host), port_(port), password_(password), context_(nullptr)
        {
        }

        RedisConnection::~RedisConnection()
        {
            Close();
        }

        bool RedisConnection::IsValid() const
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            return ctx->err == 0;
        }

        bool RedisConnection::Ping()
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "PING");

            if (!reply)
            {
                LOG_ERROR << "Redis PING failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_STATUS &&
                            strcmp(reply->str, "PONG") == 0);

            freeReplyObject(reply);

            return success;
        }

        void RedisConnection::Close()
        {
            if (context_)
            {
                redisFree(static_cast<redisContext *>(context_));
                context_ = nullptr;
                LOG_DEBUG << "Redis connection closed";
            }
        }

        bool RedisConnection::Reconnect()
        {
            Close();

            LOG_INFO << "Connecting to Redis " << host_ << ":" << port_;

            // 连接Redis
            redisContext *ctx = redisConnect(host_.c_str(), port_);

            if (!ctx)
            {
                LOG_ERROR << "Redis connect failed: cannot allocate context";
                return false;
            }

            if (ctx->err)
            {
                LOG_ERROR << "Redis connect failed: " << ctx->errstr;
                redisFree(ctx);
                return false;
            }

            // 如果有密码，进行认证
            if (!password_.empty())
            {
                redisReply *reply = (redisReply *)redisCommand(ctx, "AUTH %s", password_.c_str());

                if (!reply)
                {
                    LOG_ERROR << "Redis AUTH failed: " << ctx->errstr;
                    redisFree(ctx);
                    return false;
                }

                if (reply->type == REDIS_REPLY_ERROR)
                {
                    LOG_ERROR << "Redis AUTH failed: " << reply->str;
                    freeReplyObject(reply);
                    redisFree(ctx);
                    return false;
                }

                freeReplyObject(reply);
                LOG_DEBUG << "Redis AUTH successful";
            }

            context_ = ctx;
            LOG_INFO << "Redis connected successfully";

            return true;
        }

        // Redis命令实现

        bool RedisConnection::Set(const std::string &key, const std::string &value, int expire_seconds)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply;

            if (expire_seconds > 0)
            {
                // SET key value EX seconds
                reply = (redisReply *)redisCommand(ctx, "SET %s %s EX %d",
                                                   key.c_str(), value.c_str(), expire_seconds);
            }
            else
            {
                reply = (redisReply *)redisCommand(ctx, "SET %s %s", key.c_str(), value.c_str());
            }

            if (!reply)
            {
                LOG_ERROR << "Redis SET failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "OK") == 0);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::Get(const std::string &key, std::string &value)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "GET %s", key.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis GET failed: " << ctx->errstr;
                return false;
            }

            bool success = false;
            if (reply->type == REDIS_REPLY_STRING)
            {
                value = std::string(reply->str, reply->len);
                success = true;
            }
            else if (reply->type == REDIS_REPLY_NIL)
            {
                // Key不存在
                success = false;
            }

            freeReplyObject(reply);
            return success;
        }

        bool RedisConnection::Del(const std::string &key)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "DEL %s", key.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis DEL failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER && reply->integer > 0);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::Exists(const std::string &key)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "EXISTS %s", key.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis EXISTS failed: " << ctx->errstr;
                return false;
            }

            bool exists = (reply->type == REDIS_REPLY_INTEGER && reply->integer == 1);
            freeReplyObject(reply);

            return exists;
        }

        bool RedisConnection::Expire(const std::string &key, int seconds)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "EXPIRE %s %d", key.c_str(), seconds);

            if (!reply)
            {
                LOG_ERROR << "Redis EXPIRE failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER && reply->integer == 1);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::Incr(const std::string &key, long long &out_value)
        {
            out_value = 0;
            if (!context_)
            {
                return false;
            }
            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "INCR %s", key.c_str());
            if (!reply)
            {
                LOG_ERROR << "Redis INCR failed: " << ctx->errstr;
                return false;
            }
            bool success = false;
            if (reply->type == REDIS_REPLY_INTEGER)
            {
                out_value = reply->integer;
                success = true;
            }
            freeReplyObject(reply);
            return success;
        }

        bool RedisConnection::IncrBy(const std::string &key, long long delta, long long &out_value)
        {
            out_value = 0;
            if (!context_)
            {
                return false;
            }
            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "INCRBY %s %lld", key.c_str(), (long long)delta);
            if (!reply)
            {
                LOG_ERROR << "Redis INCRBY failed: " << ctx->errstr;
                return false;
            }
            bool success = false;
            if (reply->type == REDIS_REPLY_INTEGER)
            {
                out_value = reply->integer;
                success = true;
            }
            freeReplyObject(reply);
            return success;
        }

        // 列表操作

        bool RedisConnection::LPush(const std::string &key, const std::string &value)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "LPUSH %s %s", key.c_str(), value.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis LPUSH failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::RPush(const std::string &key, const std::string &value)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "RPUSH %s %s", key.c_str(), value.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis RPUSH failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::LPop(const std::string &key, std::string &value)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "LPOP %s", key.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis LPOP failed: " << ctx->errstr;
                return false;
            }

            bool success = false;
            if (reply->type == REDIS_REPLY_STRING)
            {
                value = std::string(reply->str, reply->len);
                success = true;
            }

            freeReplyObject(reply);
            return success;
        }

        bool RedisConnection::RPop(const std::string &key, std::string &value)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "RPOP %s", key.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis RPOP failed: " << ctx->errstr;
                return false;
            }

            bool success = false;
            if (reply->type == REDIS_REPLY_STRING)
            {
                value = std::string(reply->str, reply->len);
                success = true;
            }

            freeReplyObject(reply);
            return success;
        }

        // 集合操作

        bool RedisConnection::SAdd(const std::string &key, const std::string &member)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "SADD %s %s", key.c_str(), member.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis SADD failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::SRem(const std::string &key, const std::string &member)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "SREM %s %s", key.c_str(), member.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis SREM failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER && reply->integer > 0);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::SIsMember(const std::string &key, const std::string &member)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "SISMEMBER %s %s", key.c_str(), member.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis SISMEMBER failed: " << ctx->errstr;
                return false;
            }

            bool is_member = (reply->type == REDIS_REPLY_INTEGER && reply->integer == 1);
            freeReplyObject(reply);

            return is_member;
        }

        // 哈希操作

        bool RedisConnection::HSet(const std::string &key, const std::string &field, const std::string &value)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "HSET %s %s %s",
                                                           key.c_str(), field.c_str(), value.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis HSET failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::HGet(const std::string &key, const std::string &field, std::string &value)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "HGET %s %s", key.c_str(), field.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis HGET failed: " << ctx->errstr;
                return false;
            }

            bool success = false;
            if (reply->type == REDIS_REPLY_STRING)
            {
                value = std::string(reply->str, reply->len);
                success = true;
            }

            freeReplyObject(reply);
            return success;
        }

        bool RedisConnection::HDel(const std::string &key, const std::string &field)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "HDEL %s %s", key.c_str(), field.c_str());

            if (!reply)
            {
                LOG_ERROR << "Redis HDEL failed: " << ctx->errstr;
                return false;
            }

            bool success = (reply->type == REDIS_REPLY_INTEGER && reply->integer > 0);
            freeReplyObject(reply);

            return success;
        }

        bool RedisConnection::GetInfo(std::string &info_output)
        {
            if (!context_)
            {
                return false;
            }

            redisContext *ctx = static_cast<redisContext *>(context_);
            redisReply *reply = (redisReply *)redisCommand(ctx, "INFO");

            if (!reply)
            {
                LOG_ERROR << "Redis INFO failed: " << ctx->errstr;
                return false;
            }

            bool success = false;
            if (reply->type == REDIS_REPLY_STRING)
            {
                info_output = std::string(reply->str, reply->len);
                success = true;
            }

            freeReplyObject(reply);
            return success;
        }

        // RedisPool实现

        RedisPool &RedisPool::Instance()
        {
            static RedisPool instance(
                std::getenv("REDIS_HOST") ? std::getenv("REDIS_HOST") : "127.0.0.1",
                std::getenv("REDIS_PORT") ? std::atoi(std::getenv("REDIS_PORT")) : 6379,
                std::getenv("REDIS_PASSWORD") ? std::getenv("REDIS_PASSWORD") : "",
                5, 20);
            return instance;
        }

        RedisPool::RedisPool(const std::string &host, int port, const std::string &password,
                             size_t min_size, size_t max_size)
            : ConnectionPool<RedisConnection>(min_size, max_size, 3000), host_(host), port_(port), password_(password)
        {

            LOG_INFO << "RedisPool created: " << host << ":" << port
                     << " (min=" << min_size << ", max=" << max_size << ")";
        }

        RedisPool::ConnectionPtr RedisPool::CreateConnection()
        {
            try
            {
                auto conn = std::make_shared<RedisConnection>(host_, port_, password_);

                if (!conn->Reconnect())
                {
                    LOG_ERROR << "Failed to create Redis connection";
                    return nullptr;
                }

                return conn;
            }
            catch (const std::exception &e)
            {
                LOG_ERROR << "Exception creating Redis connection: " << e.what();
                return nullptr;
            }
        }

    } // namespace database
} // namespace file_server
